/**
 * MCU GPIO 驱动适配层
 * Copyright (c) 2021 深圳市智辰科技有限公司
 * All rights reserved.
 */
#include "stdc.h"
#include "driver_iomux.h"
#include "driver_pmu.h"
#include "patch/patch.driver_pmu.h"
#include "driver_gpio.h"
#include "driver_system.h"
#include "driver_exti.h"
#include "driver_pwm.h"
#include "patch/patch.driver_pwm.h"

#include "nonos.h"
#include "gpio.h"

/*
typedef struct {
	enum system_port_t port;
	enum system_port_bit_t bit;
	uint32_t mask;
	int mux;
	enum exti_channel_t exti_ch;
	enum exti_mux_t exti_mux;
	enum pwm_channel_t pwm_ch;
} gpio_map_t;

const static gpio_map_t _gpio_map[] = {
	//  端口          PIN        NAME     GPIO 复用名      EXTI 通道/复用名      PWM 通道
	{ GPIO_PORT_A, GPIO_BIT_0, GPIO_PA0, PORTA0_FUNC_A0, EXTI_0 , EXTI_0_PA0 , PWM_CHANNEL_0 },
	{ GPIO_PORT_A, GPIO_BIT_1, GPIO_PA1, PORTA1_FUNC_A1, EXTI_1 , EXTI_1_PA1 , PWM_CHANNEL_1 },
	{ GPIO_PORT_A, GPIO_BIT_2, GPIO_PA2, PORTA2_FUNC_A2, EXTI_2 , EXTI_2_PA2 , PWM_CHANNEL_2 },
	{ GPIO_PORT_A, GPIO_BIT_3, GPIO_PA3, PORTA3_FUNC_A3, EXTI_3 , EXTI_3_PA3 , PWM_CHANNEL_3 },
	{ GPIO_PORT_A, GPIO_BIT_4, GPIO_PA4, PORTA4_FUNC_A4, EXTI_4 , EXTI_4_PA4 , PWM_CHANNEL_4 },
	{ GPIO_PORT_A, GPIO_BIT_5, GPIO_PA5, PORTA5_FUNC_A5, EXTI_5 , EXTI_5_PA5 , PWM_CHANNEL_5 },
	{ GPIO_PORT_A, GPIO_BIT_6, GPIO_PA6, PORTA6_FUNC_A6, EXTI_6 , EXTI_6_PA6 , PWM_CHANNEL_0 },
	{ GPIO_PORT_A, GPIO_BIT_7, GPIO_PA7, PORTA7_FUNC_A7, EXTI_7 , EXTI_7_PA7 , PWM_CHANNEL_1 },
	                                                                           
	{ GPIO_PORT_B, GPIO_BIT_0, GPIO_PB0, PORTB0_FUNC_B0, EXTI_8 , EXTI_8_PB0 , PWM_CHANNEL_0 },
	{ GPIO_PORT_B, GPIO_BIT_1, GPIO_PB1, PORTB1_FUNC_B1, EXTI_9 , EXTI_9_PB1 , PWM_CHANNEL_1 },
	{ GPIO_PORT_B, GPIO_BIT_2, GPIO_PB2, PORTB2_FUNC_B2, EXTI_10, EXTI_10_PB2, PWM_CHANNEL_2 },
	{ GPIO_PORT_B, GPIO_BIT_3, GPIO_PB3, PORTB3_FUNC_B3, EXTI_11, EXTI_11_PB3, PWM_CHANNEL_3 },
	{ GPIO_PORT_B, GPIO_BIT_4, GPIO_PB4, PORTB4_FUNC_B4, EXTI_12, EXTI_12_PB4, PWM_CHANNEL_4 },
	{ GPIO_PORT_B, GPIO_BIT_5, GPIO_PB5, PORTB5_FUNC_B5, EXTI_13, EXTI_13_PB5, PWM_CHANNEL_5 },
	{ GPIO_PORT_B, GPIO_BIT_6, GPIO_PB6, PORTB6_FUNC_B6, EXTI_14, EXTI_14_PB6, PWM_CHANNEL_2 },
	{ GPIO_PORT_B, GPIO_BIT_7, GPIO_PB7, PORTB7_FUNC_B7, EXTI_15, EXTI_15_PB7, PWM_CHANNEL_3 },
	                                                                           
	{ GPIO_PORT_C, GPIO_BIT_0, GPIO_PC0, PORTC0_FUNC_C0, EXTI_0 , EXTI_0_PC0 , PWM_CHANNEL_0 },
	{ GPIO_PORT_C, GPIO_BIT_1, GPIO_PC1, PORTC1_FUNC_C1, EXTI_1 , EXTI_1_PC1 , PWM_CHANNEL_1 },
	{ GPIO_PORT_C, GPIO_BIT_2, GPIO_PC2, PORTC2_FUNC_C2, EXTI_2 , EXTI_2_PC2 , PWM_CHANNEL_2 },
	{ GPIO_PORT_C, GPIO_BIT_3, GPIO_PC3, PORTC3_FUNC_C3, EXTI_3 , EXTI_3_PC3 , PWM_CHANNEL_3 },
	{ GPIO_PORT_C, GPIO_BIT_4, GPIO_PC4, PORTC4_FUNC_C4, EXTI_4 , EXTI_4_PC4 , PWM_CHANNEL_4 },
	{ GPIO_PORT_C, GPIO_BIT_5, GPIO_PC5, PORTC5_FUNC_C5, EXTI_5 , EXTI_5_PC5 , PWM_CHANNEL_5 },
	{ GPIO_PORT_C, GPIO_BIT_6, GPIO_PC6, PORTC6_FUNC_C6, EXTI_6 , EXTI_6_PC6 , PWM_CHANNEL_4 },
	{ GPIO_PORT_C, GPIO_BIT_7, GPIO_PC7, PORTC7_FUNC_C7, EXTI_7 , EXTI_7_PC7 , PWM_CHANNEL_5 },
	                                                                           
	{ GPIO_PORT_D, GPIO_BIT_0, GPIO_PD0, PORTD0_FUNC_D0, EXTI_8 , EXTI_8_PD0 , PWM_CHANNEL_0 },
	{ GPIO_PORT_D, GPIO_BIT_1, GPIO_PD1, PORTD1_FUNC_D1, EXTI_9 , EXTI_9_PD1 , PWM_CHANNEL_1 },
	{ GPIO_PORT_D, GPIO_BIT_2, GPIO_PD2, PORTD2_FUNC_D2, EXTI_10, EXTI_10_PD2, PWM_CHANNEL_2 },
	{ GPIO_PORT_D, GPIO_BIT_3, GPIO_PD3, PORTD3_FUNC_D3, EXTI_11, EXTI_11_PD3, PWM_CHANNEL_3 },
	{ GPIO_PORT_D, GPIO_BIT_4, GPIO_PD4, PORTD4_FUNC_D4, EXTI_12, EXTI_12_PD4, PWM_CHANNEL_4 },
	{ GPIO_PORT_D, GPIO_BIT_5, GPIO_PD5, PORTD5_FUNC_D5, EXTI_13, EXTI_13_PD5, PWM_CHANNEL_5 },
	{ GPIO_PORT_D, GPIO_BIT_6, GPIO_PD6, PORTD6_FUNC_D6, EXTI_14, EXTI_14_PD6, PWM_CHANNEL_0 },
	{ GPIO_PORT_D, GPIO_BIT_7, GPIO_PD7, PORTD7_FUNC_D7, EXTI_15, EXTI_15_PD7, PWM_CHANNEL_1 },
};
*/

std_err_t GPIO_Config(gpio_num_t num, gpio_mode_t mode) {
	if(num >= GPIO_NUM_MAX) { return STD_ERR_INVALID_ARG; }
	if(mode >= GPIO_MODE_MAX) { return STD_ERR_INVALID_ARG; }

	enum system_port_t port = (enum system_port_t) (num >> 3);
	enum system_port_bit_t bit = (enum system_port_bit_t) (num & 0x07);
	uint8_t pull = (mode == GPIO_MODE_IN_PU || mode == GPIO_MODE_OUT_PP);
	uint8_t dir = (mode == GPIO_MODE_IN_PU || mode == GPIO_MODE_IN); // OUT = 0, IN = 1;

	pmu_set_pin_to_CPU  (port, BV(bit));
	system_set_port_mux (port, bit, 0);
	gpio_set_dir        (port, bit, dir);
	system_set_port_pull(BV(num), pull);
	
	return STD_ERR_OK;
}

void GPIOS_Config(uint32_t pins, gpio_mode_t mode) {
	for(int i=0; i<32; i++) { 
		if(pins & BV(i)) { GPIO_Config((gpio_num_t)i, mode); }
	}
}

std_err_t GPIO_SetLevel(gpio_num_t num, uint8_t level) {
	if(num >= GPIO_NUM_MAX) { return STD_ERR_INVALID_ARG; }
	enum system_port_t port = (enum system_port_t) (num >> 3);
	enum system_port_bit_t bit = (enum system_port_bit_t) (num & 0x07);
	gpio_set_pin_value(port, bit, (level != 0));
	return STD_ERR_OK;
}

void GPIOS_SetLevel(uint32_t pins, uint8_t level) {
	for(int i=0; i<32; i++) {
		if(pins & BV(i)) { GPIO_SetLevel((gpio_num_t)i, level); }
	}
}

uint8_t GPIO_GetLevel(gpio_num_t num) {
	enum system_port_t port = (enum system_port_t) (num >> 3);
	enum system_port_bit_t bit = (enum system_port_bit_t) (num & 0x07);
	return gpio_get_pin_value(port, bit);
}

uint32_t GPIOS_GetLevel(uint32_t pins) {
	uint32_t val = 0;
	for(int i=0; i<32; i++) {
		if(pins & BV(i)) { val |= (GPIO_GetLevel((gpio_num_t)i) << i); }
	}
	return val;
}

// 双向端口方向: 0-输入; 1-输出
// 注意：默认为输入，对应 GPIO 口必须初始化为 IN_PU
static uint32_t bidi_port_dir = 0;

uint8_t GPIO_BidiIn(gpio_num_t num) {
	// 如果端口未配置，则先配置端口
	if(bidi_port_dir & num) {
		// 双向端口配置为上拉输入
		GPIO_Config(num, GPIO_MODE_IN_PU);
		bidi_port_dir &= (~num);
	}
	return GPIO_GetLevel(num);
}

std_err_t GPIO_BidiOut(gpio_num_t num, uint8_t level) {
	// 如果端口未配置，则先配置端口
	if(!(bidi_port_dir & num)) {
		// 先设置好 GPIO 输出电平，以免在配置 GPIO 时产生意外的毛刺
		GPIO_SetLevel(num, level);
		// 双向端口配置为推挽输出
		GPIO_Config(num, GPIO_MODE_OUT_PP);
		bidi_port_dir |= num;
	}
	return GPIO_SetLevel(num, level);
}

// 保存各通道 EXTI_INT_TYPE 配置信息
// 因为 EXTI_INT_TYPE 配置寄存器只能写，读出来之后全是0！
static uint8_t ext_int_types[16];

std_err_t GPIO_EXTI_Enable(gpio_num_t num, gpio_intr_type_t intrType, uint16_t debounceUs) {
	if(num >= GPIO_NUM_MAX) { return STD_ERR_INVALID_ARG; }
	if(intrType >= GPIO_INTR_MAX) { return STD_ERR_INVALID_ARG; }
	
	enum exti_channel_t exti_ch = (enum exti_channel_t) (num & 0x0F);
	enum exti_mux_t exti_mux = (enum exti_mux_t) ((num < 16) ? 0 : 1);
	enum ext_int_type_t exti_type = (enum ext_int_type_t) intrType;
	
	// 设置 EXTI 通道与 GPIO_EXTI 信号的复用关系
	ext_int_set_port_mux(exti_ch, exti_mux);
	// 设置 EXTI 通道的中断触发类型
	ext_int_set_type(exti_ch, exti_type);
	// 保存到配置数组
	ext_int_types[exti_ch] = exti_type;
	
	// 如果是电平触发，则还可以设置 Debunce(防抖) 时间
	if(intrType == GPIO_INTR_LOW || intrType == GPIO_INTR_HIGH) {
		uint16_t deb_clk_div = 0;
		uint8_t deb_count = 0;
		if(debounceUs) {
			// Debunce clock prescaler value
			// Debunce clock = PCLK/(1 + Prescaler value)
			deb_clk_div = 4800 - 1;       // 每个计数周期 100us
			deb_count = debounceUs / 100; // 持续多少个计数周期
		}
		ext_int_set_control(exti_ch, deb_clk_div, deb_count);
	}
	
	// EXTI 通道中断使能
	ext_int_enable(exti_ch);
	
	// !!! 注意：要在 bsp 初始化中，统一设置 EXTI 中断向量优先级，并启用中断 !!!
	//NVIC_SetPriority(EXTI_IRQn, 4);
	//NVIC_EnableIRQ(EXTI_IRQn);
	
	return STD_ERR_OK;
}

void GPIOS_EXTI_Enable(uint32_t pins, gpio_intr_type_t intrType, uint16_t debounceUs) {
	for(int i=0; i<32; i++) {
		if(pins & BV(i)) { GPIO_EXTI_Enable((gpio_num_t)i, intrType, debounceUs); }
	}
}

std_err_t GPIO_EXTI_ToggleTrigLevel(gpio_num_t num) {
	if(num >= GPIO_NUM_MAX) { return STD_ERR_INVALID_ARG; }
	enum exti_channel_t exti_ch = (enum exti_channel_t) (num & 0x0F);
	
	// 取得当前通道触发类型
	enum ext_int_type_t exti_type = (enum ext_int_type_t) ext_int_types[exti_ch];
	
	// 反向
	/**/ if(exti_type == EXT_INT_TYPE_LOW ) { exti_type = EXT_INT_TYPE_HIGH; }
	else if(exti_type == EXT_INT_TYPE_HIGH) { exti_type = EXT_INT_TYPE_LOW;  }
	else if(exti_type == EXT_INT_TYPE_POS ) { exti_type = EXT_INT_TYPE_NEG;  }
	else if(exti_type == EXT_INT_TYPE_NEG ) { exti_type = EXT_INT_TYPE_POS;  }
	// 重新设置中断类型
	ext_int_set_type(exti_ch, exti_type);
	// 保存到配置数组
	ext_int_types[exti_ch] = exti_type;
	
	return STD_ERR_OK;
}

std_err_t GPIO_EXTI_Disable(gpio_num_t num) {
	if(num >= GPIO_NUM_MAX) { return STD_ERR_INVALID_ARG; }	
	enum exti_channel_t exti_ch = (enum exti_channel_t) (num & 0x0F);
	
	// EXTI 通道中断关闭
	ext_int_enable(exti_ch);
	
	return STD_ERR_OK;
}

uint8_t GPIO_EXTI_Flag(gpio_num_t num) {
	if(num >= GPIO_NUM_MAX) { return 0; }
	enum exti_channel_t exti_ch = (enum exti_channel_t) (num & 0x0F);
	
	// 获取中断源
    uint32_t status = ext_int_get_src();
	// 判断中断源标志位
	return (BV(exti_ch) & status) != 0;
}

std_err_t GPIO_EXTI_Clear(gpio_num_t num) {
	if(num >= GPIO_NUM_MAX) { return STD_ERR_INVALID_ARG; }
	enum exti_channel_t exti_ch = (enum exti_channel_t) (num & 0x0F);
	
	// 清除中断标志位
    ext_int_clear(BV(exti_ch));
	
	return STD_ERR_OK;
}

static enum pwm_channel_t getPwmChannel(gpio_num_t num) {
	enum system_port_t port = (enum system_port_t) (num >> 3);
	enum system_port_bit_t bit = (enum system_port_bit_t) (num & 0x07);
	return (enum pwm_channel_t) ((bit < 6) ? bit : ((2*port + (bit % 6)) % 6));
}

std_err_t GPIO_ConfigAsPwm(gpio_num_t num, uint32_t freq, uint32_t duty) {
	if(num >= GPIO_NUM_MAX) { return STD_ERR_INVALID_ARG; }
	
	// 根据引脚获取其它信息
	enum system_port_t port = (enum system_port_t) (num >> 3);
	enum system_port_bit_t bit = (enum system_port_bit_t) (num & 0x07);
	enum pwm_channel_t ch = getPwmChannel(num);
	
	// 配置 PWM 波形
	pwm_update2(ch, freq, duty);
	// 配置为 PWM 输出
	system_set_port_mux(port, bit, 0x03);
	
	return STD_ERR_OK;
}

std_err_t GPIO_PwmSetDuty(gpio_num_t num, uint32_t duty) {
	if(num >= GPIO_NUM_MAX) { return STD_ERR_INVALID_ARG; }
	enum pwm_channel_t ch = getPwmChannel(num);
	pwm_set_duty(ch, duty);
	return STD_ERR_OK;
}

uint32_t GPIO_PwmGetDuty(gpio_num_t num) {
	if(num >= GPIO_NUM_MAX) { return 0; }
	enum pwm_channel_t ch = getPwmChannel(num);
	return pwm_get_duty(ch);
}

std_err_t GPIO_PwmStart(gpio_num_t num) {
	if(num >= GPIO_NUM_MAX) { return STD_ERR_INVALID_ARG; }
	enum pwm_channel_t ch = getPwmChannel(num);
	pwm_start(ch);
	return STD_ERR_OK;
}

std_err_t GPIO_PwmStop(gpio_num_t num) {
	if(num >= GPIO_NUM_MAX) { return STD_ERR_INVALID_ARG; }
	enum pwm_channel_t ch = getPwmChannel(num);
	pwm_stop(ch);
	return STD_ERR_OK;
}

// LED1 做为电平输出时的当前电平
static uint8_t _gpio1_level = 0;

std_err_t gpio_led1_set_level(uint8_t level) {
	_gpio1_level = level;
	pmu_set_led1_value(_gpio1_level);
	return STD_ERR_OK;
}

uint8_t gpio_led1_get_level(void) { 
	return _gpio1_level;
}

// LED2 做为电平输出时的当前电平
static uint8_t _gpio2_level = 0;

std_err_t gpio_led2_set_level(uint8_t level) {
	_gpio2_level = level;
	pmu_set_led2_value(_gpio2_level);
	return STD_ERR_OK;
}

uint8_t gpio_led2_get_level(void) { 
	return _gpio2_level;
}

std_err_t GPIO_PMU_Config(gpio_num_t num, gpio_mode_t mode) {
	if(num >= GPIO_NUM_MAX) { return STD_ERR_INVALID_ARG; }
	if(mode >= GPIO_MODE_MAX) { return STD_ERR_INVALID_ARG; }

	enum system_port_t port = (enum system_port_t) (num >> 3);
	enum system_port_bit_t bit = (enum system_port_bit_t) (num & 0x07);
	uint8_t pull = (mode == GPIO_MODE_IN_PU || mode == GPIO_MODE_OUT_PP);
	uint8_t dir = (mode == GPIO_MODE_IN_PU || mode == GPIO_MODE_IN); // OUT = 0, IN = 1;
	
	pmu_set_pin_to_PMU(port, BV(bit));
	pmu_set_port_mux  (port, bit, PMU_PORT_MUX_GPIO);
	pmu_set_pin_dir   (port, BV(bit), dir);
	pmu_set_pin_pull  (port, BV(bit), pull);
	
	return STD_ERR_OK;
}

void GPIOS_PMU_Config(uint32_t pins, gpio_mode_t mode) {
	for(int i=0; i<32; i++) {
		if(pins & BV(i)) { GPIO_PMU_Config((gpio_num_t)i, mode); }
	}
}

std_err_t GPIO_PMU_SetLevel(gpio_num_t num, uint8_t level) {
	if(num >= GPIO_NUM_MAX) { return STD_ERR_INVALID_ARG; }
	enum system_port_t port = (enum system_port_t) (num >> 3);
	enum system_port_bit_t bit = (enum system_port_bit_t) (num & 0x07);	
	pmu_set_gpio_value(port, BV(bit), (level != 0));	
	return STD_ERR_OK;
}

void GPIOS_PMU_SetLevel(uint32_t pins, uint8_t level) {
	for(int i=0; i<32; i++) {
		if(pins & BV(i)) { GPIO_PMU_SetLevel((gpio_num_t)i, level); }
	}
}

uint8_t GPIO_PMU_GetLevel(gpio_num_t num) {
	if(num >= GPIO_NUM_MAX) { return 0; }
	enum system_port_t port = (enum system_port_t) (num >> 3);
	enum system_port_bit_t bit = (enum system_port_bit_t) (num & 0x07);
	return pmu_get_gpio_value(port, bit);
}

uint32_t GPIOS_PMU_GetLevel(uint32_t pins) {
	uint32_t val = 0;
	for(int i=0; i<32; i++) {
		if(pins & BV(i)) { val |= (GPIO_PMU_GetLevel((gpio_num_t)i) << i); }
	}
	return val;
}

/// 当前各 GPIO 口的唤醒使能配置
static uint32_t _pmuWakeupEnabledGpios = 0;

std_err_t GPIO_PMU_EnableWakeup(gpio_num_t num) {
	if(num >= GPIO_NUM_MAX) { return STD_ERR_INVALID_ARG; }
	_pmuWakeupEnabledGpios |= BV(num);
	pmu_port_wakeup_func_clear(0xFFFFFFFF);
	pmu_port_wakeup_func_set(_pmuWakeupEnabledGpios);
	return STD_ERR_OK;
}

void GPIOS_PMU_EnableWakeup(uint32_t pins) {
	_pmuWakeupEnabledGpios |= pins;
	pmu_port_wakeup_func_clear(0xFFFFFFFF);
	pmu_port_wakeup_func_set(_pmuWakeupEnabledGpios);
}

std_err_t GPIO_PMU_DisableWakeup(gpio_num_t num) {
	if(num >= GPIO_NUM_MAX) { return STD_ERR_INVALID_ARG; }
	_pmuWakeupEnabledGpios &= (~BV(num));
	pmu_port_wakeup_func_clear(0xFFFFFFFF);
	pmu_port_wakeup_func_set(_pmuWakeupEnabledGpios);
	return STD_ERR_OK;
}

void GPIOS_PMU_DisableWakeup(uint32_t pins) {
	_pmuWakeupEnabledGpios &= (~pins);
	pmu_port_wakeup_func_clear(0xFFFFFFFF);
	pmu_port_wakeup_func_set(_pmuWakeupEnabledGpios);
}
